-
Notifications
You must be signed in to change notification settings - Fork 344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Token propagation E2E tests. #581
Token propagation E2E tests. #581
Conversation
In the end, I haven't needed to move anything on the image, it works if I add to the If the |
0bc6efa
to
ce44015
Compare
Is this ready for review? If so, there are a couple of things that needs to be cleaned up first, like commented out code and the user change in the |
@jpkrohling Ahh! there is small details that I need to fix, (one of them is the things you pointed in your comment) ;) Thanks |
0d315fd
to
d498565
Compare
@jpkrohling now is ready |
768a43d
to
273a566
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, looks good to me. There's only one change I'd like to see, about the random string generation. The e2e tests should be reviewed by @kevinearls, and once he gives his approval, mine will follow.
pkg/util/util.go
Outdated
_, err := rand.Read(randString) | ||
if err != nil { | ||
// If we cannot generate random, return fixed. | ||
return "ncNDoqLGrayxXzxTn5ANbOXZp3qXd0LA" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return an error instead, and let the caller decide. At the very least, there should be a WARN in the logs about this. It was really bad that I didn't realize that "SECRET" should have been a real random string...
pkg/inject/oauth_proxy.go
Outdated
args = append(args, | ||
"--pass-access-token=true", | ||
"--pass-user-bearer-token=true", | ||
"--scope=user:info user:check-access user:list-projects", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spaces as separators? That's odd!
# install and run OCP | ||
sudo docker cp $(docker create docker.io/openshift/origin:$OPENSHIFT_VERSION):/bin/oc /usr/local/bin/oc | ||
oc cluster up --version=$OPENSHIFT_VERSION | ||
|
||
oc cluster up --version=$OPENSHIFT_VERSION --public-hostname=${HOST_IP}.nip.io |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @kevinearls
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted the test does not run for me on OCP 4.2
defer resp.Body.Close() | ||
return true, nil | ||
}) | ||
require.NoError(t, err, "Token propagation tmake est failed") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo?
|
||
client, err := oAuthAuthorization(queryHost, "user-test-token", "any") | ||
|
||
require.NoError(t, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test is failing for me here when I use a real OCP 4.2 OpenShift cluster. Have you been testing against minishift? I think you probably need to use the actual route here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't tested against OCP 4.2/minishift, I'll give it a try and fix what is failing. Thanks!
b14bdf9
to
d22bdfd
Compare
.ci/run-e2e-tests.sh
Outdated
oc create user user-test-token | ||
oc adm policy add-cluster-role-to-user cluster-admin user-test-token | ||
# for ocp 4.2 | ||
htpasswd -c -B -b users.htpasswd user-test-token any |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a better idea to just ship the actual file instead of generating the file at the CI.
.ci/run-e2e-tests.sh
Outdated
then | ||
echo "Running token propagation tests" | ||
oc create user user-test-token | ||
oc adm policy add-cluster-role-to-user cluster-admin user-test-token |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The bad thing about using "root-like" permissions in a test is that you might miss the cases where the user might not have the appropriate permissions. Not sure it's relevant for your test here, but in general, we should aim to have the minimum set of permissions possible.
args := []string{ | ||
"--cookie-secret=SECRET", | ||
fmt.Sprintf("--cookie-secret=%s", secret), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This generates secrets containing chars like "+" and "/". How are they translated into the final YAML? Do they cause any troubles? I don't think it does, but it's good to have positive confirmation (and perhaps a test). Example of random strings I got from this code locally:
pZ8F0imysFsTwJ9kF8U92g==
ILmvfqWW/+bLEokY06NHig==
Zmc+jX2VWXkxsB98AIYwDg==
xb/fYVT6UHxYpNJXhjR7RQ==
pkg/storage/elasticsearch.go
Outdated
@@ -38,6 +38,53 @@ type ElasticsearchDeployment struct { | |||
Secrets []corev1.Secret | |||
} | |||
|
|||
func (ed *ElasticsearchDeployment) injectArguments(container *corev1.Container) { | |||
container.Args = append(container.Args, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably a matter of taste, but do you need to split this line here? The next line seems small enough to fit here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no I don't need to split the line not sure why I did it, or may be the make format did it for me? Anyway I will change it.
container.Args = append(container.Args, "--es.tls=true") | ||
} | ||
|
||
container.Args = append(container.Args, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't these options be added only if es.tls=true
?
|
||
} | ||
|
||
func (suite *TokenPropagationTestSuite) TearDownSuite() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need this func if it's empty? The commented out code should be removed anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah this should not be empty, I commented those lines on my local but shouldn't be commented.
} | ||
|
||
err := wait.Poll(retryInterval, timeout, func() (done bool, err error) { | ||
req, err := http.NewRequest(http.MethodGet, suite.queryServiceEndPoint, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Check that error is nil, and return early in case of errors (return false, err
). Otherwise, it will fail in the next line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this wasn't done yet.
|
||
/* Try to reach query endpoint */ | ||
err = wait.Poll(retryInterval, timeout, func() (done bool, err error) { | ||
req, err := http.NewRequest(http.MethodGet, suite.queryServiceEndPoint, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as before: make the test more resilient, otherwise it might become annoying to all future PRs ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably want to check the err
objects in this method and log their causes, without hard failing.
Jar: cookieJar, | ||
} | ||
/* Start oauth */ | ||
resp, err := client.Get("https://" + host + "/oauth/start") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kinda of the same as previous comments, but you probably just want to retry in case of some well-known networking problems, like timeouts.
var req *http.Request | ||
/* Submit form */ | ||
if hasForm(responseBytes) { | ||
req = getLoginFormRequest(responseBytes, resp.Request.URL, user, pass) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For OpenShift, do you really need to parse the form and simulate a real user there? Can't you just send the user:pass with basic auth? I think the Maistra components to send the requests via basic auth.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, I can get a token an pass it to query service, but I don't know how to do it and test the whole thing using oauth proxy sidecar, Can we use basic authentication to and set the oauth proxy cookie? May be I'm missing something. Or we don't need to test using the sidecar?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maistra QE can confirm, but I understand that you can send basic auth and the proxy will do all the token negotiation based on it. Jaeger will see a token, just like if it was a browser session.
d22bdfd
to
7fd5aa4
Compare
Signed-off-by: Ruben Vargas <[email protected]>
7100f85
to
512ee05
Compare
Signed-off-by: Ruben Vargas <[email protected]>
dc777fc
to
25d8e83
Compare
Signed-off-by: Ruben Vargas <[email protected]> Signed-off-by: Ruben Vargas <[email protected]>
25d8e83
to
f100fc9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. A couple of test resiliency + negative tests requests, but this could be merged as is and get the remaining comments addressed in a follow-up PR.
pkg/inject/oauth_proxy.go
Outdated
func proxyInitArguments(jaeger *v1.Jaeger) []string { | ||
secret, err := util.GenerateProxySecret() | ||
if err != nil { | ||
jaeger.Logger().Warnf("Error generating secret: %s, fallback to fixed secret", secret) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WithError(err)
, so that the cause is logged.
} | ||
|
||
err := wait.Poll(retryInterval, timeout, func() (done bool, err error) { | ||
req, err := http.NewRequest(http.MethodGet, suite.queryServiceEndPoint, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this wasn't done yet.
APIVersion: "jaegertracing.io/v1", | ||
}, | ||
})) | ||
assert.NoError(t, framework.AddToFrameworkScheme(apis.AddToScheme, &esv1.ElasticsearchList{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps you want to use require
here instead of assert
?
|
||
/* Try to reach query endpoint */ | ||
err = wait.Poll(retryInterval, timeout, func() (done bool, err error) { | ||
req, err := http.NewRequest(http.MethodGet, suite.queryServiceEndPoint, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably want to check the err
objects in this method and log their causes, without hard failing.
|
||
client, err := oAuthAuthorization(host, username, password) | ||
|
||
err = wait.Poll(retryInterval, timeout, func() (done bool, err error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those tests seem to be missing.
@kevinearls could you give this a test in OpenShift before this gets merged? |
@jpkrohling Sure, I'll try it later today or first thing tomorrow |
@jpkrohling @rubenvp8510 I've tried this on an OCP 4.2 cluster, but unfortunately have not been able to get it to work so far. The TestTokenPropagationNoToken test passes, but TestTokenPropagationNoToken fails at line 134 with a 403. I will note a couple of issues I came across in the code, but I don't have any ideas so far on how to get this to work. |
@kevinearls I'll check, TestTokenPropagationNoToken should throws a 403 (Forbidden) , is the expected thing, I'll check other test to see why it does not pass. |
require.Equal(t, http.StatusOK, resp.StatusCode) | ||
if resp.StatusCode != http.StatusOK { | ||
return false, errors.New("Query service returns http code: " + string(resp.StatusCode)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The if is redundant here as if the require.Equal fails the test will terminate. You should pick one or the other. In general I prefer the require as it makes for more concise code and produces better error messages. However, if you prefer the if you need to change the string(resp.StatusCode) to strconv.Itoa
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I preferred required too, but I had to do in this way, so if it fails the first time due the pod not fully ready or other timing issues, the wait.Poll will retry again.
If I use required, it will fail mediately. Making this test less reliable. IMHO.
I removed the required line.
} | ||
|
||
func bindOperatorWithAuthDelegator() { | ||
roleBinding := rbac.ClusterRoleBinding{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This clusterrolebinding and the token-test-user-bind clusterrolebinding created below need to be deleted at the end of the test (in TearDownSuite()) otherwise the test will fail after the first run as these will already exist on the cluster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the observation, I've added cleaning routine for remove all created roles/role-bindings.
@rubenvp8510 Sorry, my comment about the test failure was wrong. TestTokenPropagationValidToken is the test that fails, TestTokenPropagationNoToken works correctly. Sorry about the careless cut and paste on my part. 😧 |
I ran the tests a couple of times, and sometimes fails and others not, I'll do this more reliable |
Signed-off-by: Ruben Vargas <[email protected]>
Signed-off-by: Ruben Vargas <[email protected]>
7d1c1fc
to
93df3af
Compare
Signed-off-by: Ruben Vargas <[email protected]>
1767c54
to
0bb2711
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Works for repeated runs on OCP 4.2
Thanks for being persistent, @rubenvp8510! |
Signed-off-by: Ruben Vargas [email protected]
Also, in my tests I realized that this is not gonna work if the query service has--es.use-aliase
flag enabled.